package com.zmn.plat.business.impl.price.meter3;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dto.KvDTO;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.plat.business.interfaces.price.meter3.MeterFlow3CommonBService;
import com.zmn.plat.business.interfaces.price.meter3.MeterFlow3ItemBService;
import com.zmn.plat.business.interfaces.product.ServProductBService;
import com.zmn.plat.common.constant.ItemCategoryConsts;
import com.zmn.plat.common.constant.ProductConsts;
import com.zmn.plat.common.dictionary.PlatErrorCodeDict;
import com.zmn.plat.common.dto.price.ItemTo;
import com.zmn.plat.common.dto.price.meter3.*;
import com.zmn.plat.common.exception.PlatException;
import com.zmn.plat.manager.dictionary.ProductDict;
import com.zmn.plat.model.dto.price.PriceCalDTO;
import com.zmn.plat.model.dto.product.ProductRelationDTO;
import com.zmn.plat.model.entity.category.BaseCategory;
import com.zmn.plat.model.entity.category.BaseCategoryAttributeEnum;
import com.zmn.plat.model.entity.product.ServProduct;
import com.zmn.plat.model.entity.product.tariff.ProductTariffQuery;
import com.zmn.plat.model.entity.product.tariff.item.ProductItemQuery;
import com.zmn.plat.model.entity.servcategory.ServCategory;
import com.zmn.plat.model.entity.service.group.ServiceGroup;
import com.zmn.plat.model.entity.service.tariff.ServiceTariff;
import com.zmn.plat.model.vo.product.tariff.ProductTariffVO;
import com.zmn.plat.services.interfaces.category.BaseCategoryAttributeEnumService;
import com.zmn.plat.services.interfaces.category.BaseCategoryService;
import com.zmn.plat.services.interfaces.product.ServProductService;
import com.zmn.plat.services.interfaces.product.tariff.ProductTariffService;
import com.zmn.plat.services.interfaces.product.tariff.tariffproductitem3.TariffProductItem3Service;
import com.zmn.plat.services.interfaces.servcategory.ServCategoryService;
import com.zmn.plat.services.interfaces.service.group.ServiceGroupService;
import com.zmn.plat.services.interfaces.service.tariff.ServiceTariffService;
import com.zmn.plat.utils.bean.BeanCopyUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 描述:  计价器3.0服务项价格实现类
 *
 * @author guoqiao
 * @since 2021/11/24 10:59
 */
@Slf4j
@Service
public class MeterFlow3ItemBServiceImpl implements MeterFlow3ItemBService {

    @Resource
    ServProductService servProductService;
    @Resource
    ServiceTariffService serviceTariffService;
    @Resource
    ServiceGroupService serviceGroupService;
    @Resource
    ServProductBService servProductBService;
    @Resource
    BaseCategoryService baseCategoryService;
    @Resource
    ServCategoryService servCategoryService;
    @Resource
    BaseCategoryAttributeEnumService baseCategoryAttributeEnumService;
    @Resource
    MeterFlow3CommonBService meterFlow3CommonBService;
    @Resource
    TariffProductItem3Service tariffProductItem3Service;
    @Resource
    MeterFlow3ItemBService meterFlow3ItemBService;
    @Resource
    ProductTariffService productTariffService;

    // 产品属性本地缓存
    private Cache<Integer, String> productEnuCache = CacheBuilder.newBuilder().expireAfterWrite(240, TimeUnit.HOURS).build();
    private final static String DOOR_FRR_STR = "上门费";

    @Override
    @Cacheable(cacheNames = "redis10m", key = "'price3:listItemByQuery:getDoorFeeByQuery:'+#p0", unless = "#result == null")
    public ItemDRO3 getDoorFeeByQuery(MeterBaseQuery3 query) {

        ItemPriceQuery3 itemPriceQuery3 = new ItemPriceQuery3();
        BeanCopyUtils.copyProperties(query, itemPriceQuery3);
        itemPriceQuery3.setItemCategId(ItemCategoryConsts.OTHER_ITEM);
        List<ItemDRO3> itemList = meterFlow3ItemBService.listItemByQuery(itemPriceQuery3);
        for (ItemDRO3 itemDRO3 : itemList) {
            if (Objects.equals(DOOR_FRR_STR, itemDRO3.getItemName())) {
                return itemDRO3;
            }
        }
        return null;
    }

    @Override
    public ItemDRO3 getMinItemPriceByQuery(ItemPriceQuery3 query) {

        List<ItemDRO3> itemDROList = meterFlow3ItemBService.listItemByQuery(query);
        if (CollectionUtils.isEmpty(itemDROList)) {
            return null;
        }
        ItemDRO3 itemDRO3 = itemDROList.stream().sorted(Comparator.comparing(ItemDRO3::getPrice)).findFirst().get();
        PriceCalDTO calDTO = new PriceCalDTO(query.getChannelId(), query.getCityId(), query.getBrandId(), query.getBizType(), null);
        meterFlow3CommonBService.calPriceDetail(itemDRO3, calDTO);
        return itemDRO3;
    }

    @Override
    @Cacheable(cacheNames = "redis10m", key = "'price3:listItemByQuery:ItemPriceQuery3:'+#p0", unless = "#result == null")
    public List<ItemDRO3> listItemByQuery(ItemPriceQuery3 query) {

        Integer productId = query.getProductId();
        Integer showType = query.getShowType();
        Integer channelId = query.getChannelId();
        Integer bizType = query.getBizType();
        Integer tariffId = query.getTariffId();
        ServProduct servProduct = servProductService.findByKeyWithCache(productId);
        if (Objects.isNull(servProduct)) {
            throw new PlatException(PlatErrorCodeDict.ERROR_CODE_SERVICE_PRODUCT_NULL);
        }
        if (NumberUtil.isNullOrZero(showType)) {
            showType = servProduct.getShowType();
        }

        List<ProductRelationDTO> relationDTOList = servProductBService.listProductRalationByIdListAndShowType(Arrays.asList(productId), showType);
        if (CollectionUtils.isEmpty(relationDTOList)) {
            return Collections.emptyList();
        }
        Map<Integer, ProductRelationDTO> relationDTOMap = relationDTOList.stream().collect(Collectors.toMap(e -> e.getProductId(), e -> e, (p, v) -> p));

        // 查询前台、后台、计价器产品关系并查询价格表
        List<KvDTO<Integer, Integer>> collect = relationDTOList.stream()
                .peek(e -> {
                    if (NumberUtil.isNullOrZero(tariffId)) {
                        // 查询计价器3.0价格表
                        ProductTariffTypeDRO productTariffType = meterFlow3CommonBService.getProductTariffType(channelId, e.getProductId(), bizType, ProductDict.TRIFF_TYPE_TARIFF3);
                        if (Objects.nonNull(productTariffType)) {
                            e.setTariffId(productTariffType.getTariffId());
                            e.setTariffType(productTariffType.getTariffType());
                        }
                    } else {
                        // 自带价格表id
                        ServiceTariff tariff = serviceTariffService.findByKeyWithCache(tariffId);
                        if (Objects.nonNull(tariff)) {
                            e.setTariffId(tariff.getTariffId());
                            e.setTariffType(tariff.getTariffType());
                        }
                    }
                }).filter(e -> Objects.equals(ProductDict.TRIFF_TYPE_TARIFF3, e.getTariffType()))
                .map(e -> {
                            KvDTO<Integer, Integer> map = new KvDTO(e.getProductId(), e.getTariffId());
                            return map;
                        }
                ).collect(Collectors.toList());
        log.info("后台与价格表的关系:{}", collect);
        if (CollectionUtils.isEmpty(collect)) {
            return Collections.emptyList();
        }

        // 查询item集合
        ProductItemQuery itemQuery = new ProductItemQuery();
        itemQuery.setKvList(collect);
        itemQuery.setItemCategId(query.getItemCategId());
        itemQuery.setItemIds(query.getItemList());
        List<ItemDRO3> itemDROList = Optional.ofNullable(tariffProductItem3Service.listItemPriceByQuery(itemQuery))
                .orElse(Collections.emptyList())
                .stream()
                .filter(e -> {
                    Integer priceStandard = query.getPriceStandard();
                    if (NumberUtil.isNotNullOrZero(priceStandard)) {
                        // 过滤是否一口价
                        boolean flag = Objects.equals(e.getPriceStandard(), priceStandard);
                        if (flag && Objects.equals(GlobalConsts.NO, priceStandard)) {
                            // 非一口价时返回默认服务项
                            flag = Objects.equals(e.getGeneral(), GlobalConsts.YES);
                        }
                        return flag;
                    }
                    return true;
                })
                .peek(e -> {
                    ProductRelationDTO relationDTO = relationDTOMap.get(e.getProductId());
                    e.setProductName(relationDTO.getProductName());
                    e.setServCategName(relationDTO.getServCategName());
                    e.setCategOneName(relationDTO.getCategOneName());
                    e.setCategId(relationDTO.getCategId());
                    e.setCategName(relationDTO.getCategName());
                    e.setPrice(e.getPartPrice() + e.getTrafficFee() + e.getHourFee());
                })
                .collect(Collectors.toList());
        return itemDROList;
    }

    @Override
    @Cacheable(cacheNames = "redis10m", key = "'price3:listItemByFaultIdList:MeterItemQuery3:'+#p0", unless = "#result == null")
    public List<ItemDRO3> listItemByFaultIdList(MeterItemQuery3 query) {

        ItemPriceQuery3 itemPriceQuery3 = new ItemPriceQuery3();
        BeanCopyUtils.copyProperties(query, itemPriceQuery3);
        List<ItemDRO3> itemList = meterFlow3ItemBService.listItemByQuery(itemPriceQuery3);
        List<ItemDRO3> itemDROList = new ArrayList<>(itemList.size());
        PriceCalDTO calDTO = new PriceCalDTO(query.getChannelId(), query.getCityId(), query.getBrandId(), query.getBizType(), null);
        for (ItemDRO3 itemDRO3 : itemList) {
            if (Objects.nonNull(itemDRO3.getFaultList())) {
                List<Integer> faultIdList = StringUtil.split(itemDRO3.getFaultList(), Integer.class);
                // 取并集，如果有值，则取用
                faultIdList.retainAll(query.getFaultIdList());
                if (!CollectionUtils.isEmpty(faultIdList)) {
                    // 计算系数
                    meterFlow3CommonBService.calPriceDetail(itemDRO3, calDTO);
                    itemDROList.add(itemDRO3);
                }
            }
        }
        return itemDROList;
    }

    @Override
    @Cacheable(cacheNames = "redis10m", key = "'price3:listItemQuoteByQuery:MeterQuoteQuery3:'+#p0", unless = "#result == null")
    public List<ItemDRO3> listItemQuoteByQuery(MeterQuoteQuery3 query) {

        ItemPriceQuery3 itemPriceQuery3 = new ItemPriceQuery3();
        BeanCopyUtils.copyProperties(query, itemPriceQuery3);
        List<ItemDRO3> itemDROList = meterFlow3ItemBService.listItemByQuery(itemPriceQuery3);
        if (CollectionUtils.isEmpty(itemDROList)) {
            return Collections.emptyList();
        }

        // 分组Map
        List<Integer> groupIdList = itemDROList.stream().map(m -> m.getGroupId()).distinct().collect(Collectors.toList());
        Map<Integer, ServiceGroup> groupMap = Optional.ofNullable(serviceGroupService.listByGroupIds(groupIdList))
                .orElse(Collections.emptyList())
                .stream()
                .collect(Collectors.toMap(ServiceGroup::getGroupId, r -> r));

        PriceCalDTO calDTO = new PriceCalDTO(query.getChannelId(), query.getCityId(), query.getBrandId(), query.getBizType(), null);
        // 计算系数
        itemDROList.forEach(itemDRO -> {
            meterFlow3CommonBService.calPriceDetail(itemDRO, calDTO);
            itemDRO.setGroupName(groupMap.get(itemDRO.getGroupId()).getGroupName());
            itemDRO.setGroupSort(groupMap.get(itemDRO.getGroupId()).getSort());
        });
        log.info("项目itemDRO计算价格后数据:{}", itemDROList);
        return itemDROList;
    }

    @Override
    @Cacheable(cacheNames = "redis10m", key = "'price3:listItemPriceByQuery:ItemPriceQuery3:'+#p0", unless = "#result == null")
    public List<ItemDRO3> listItemPriceByQuery(ItemPriceQuery3 query) {

        List<ItemDRO3> itemDROList = meterFlow3ItemBService.listItemByQuery(query);
        if (CollectionUtils.isEmpty(itemDROList)) {
            return Collections.emptyList();
        }
        PriceCalDTO calDTO = new PriceCalDTO(query.getChannelId(), query.getCityId(), query.getBrandId(), query.getBizType(), null);

        // 二级分类Map
        List<Integer> categTwoIdList = itemDROList.stream().map(m -> m.getCategId()).distinct().collect(Collectors.toList());
        Map<Integer, BaseCategory> categTwoMap = Optional.ofNullable(baseCategoryService.findByKeys(categTwoIdList))
                .orElse(Collections.emptyList())
                .stream()
                .collect(Collectors.toMap(BaseCategory::getCategId, r -> r));

        // 分组Map
        List<Integer> groupIdList = itemDROList.stream().map(m -> m.getGroupId()).distinct().collect(Collectors.toList());
        Map<Integer, ServiceGroup> groupMap = Optional.ofNullable(serviceGroupService.listByGroupIds(groupIdList))
                .orElse(Collections.emptyList())
                .stream()
                .collect(Collectors.toMap(ServiceGroup::getGroupId, r -> r));

        List<ItemDRO3> newItemDROList = itemDROList.parallelStream().peek(itemDRO -> {
            // 计算系数
            meterFlow3CommonBService.calPriceDetail(itemDRO, calDTO);

            // 查询后台产品
            ServProduct erpProduct = servProductService.findByKeyWithCache(itemDRO.getProductId());
            itemDRO.setProductSort(Objects.nonNull(erpProduct) ? erpProduct.getSort() : null);
            itemDRO.setCategSort(categTwoMap.get(itemDRO.getCategId()).getSort());
            itemDRO.setGroupName(groupMap.get(itemDRO.getGroupId()).getGroupName());
            itemDRO.setGroupSort(groupMap.get(itemDRO.getGroupId()).getSort());

            // 查询后台产品与价格表关系
            ProductTariffQuery productTariffQuery = new ProductTariffQuery();
            productTariffQuery.setProductId(itemDRO.getProductId());
            productTariffQuery.setTariffId(itemDRO.getTariffId());
            productTariffQuery.setStatus(GlobalConsts.YES);
            ProductTariffVO productTariff = productTariffService.listByQueryWithCache(productTariffQuery)
                    .stream()
                    .findFirst()
                    .orElse(new ProductTariffVO());
            itemDRO.setDesc(productTariff.getDesc());
            itemDRO.setSpecialDesc(productTariff.getSpecialDesc());
        }).collect(Collectors.toList());
        log.info("项目itemDRO计算价格后数据:{}", newItemDROList);
        return newItemDROList;
    }

    @Override
    @Cacheable(cacheNames = "redis10m", key = "'price3:listItemPriceTransByQuery:ItemPriceQuery3:'+#p0", unless = "#result == null")
    public List<ItemDRO3> listItemPriceTransByQuery(ItemPriceQuery3 query) {

        // 查询项目集合
        List<ItemDRO3> itemDROList = meterFlow3ItemBService.listItemByQuery(query);
        if (CollectionUtils.isEmpty(itemDROList)) {
            return Collections.emptyList();
        }
        // 查询产品
        ServProduct servProduct = servProductService.findByKeyWithCache(query.getProductId());
        // 分组Map
        List<Integer> groupIdList = itemDROList.stream().map(m -> m.getGroupId()).distinct().collect(Collectors.toList());
        Map<Integer, ServiceGroup> groupMap = Optional.ofNullable(serviceGroupService.listByGroupIds(groupIdList))
                .orElse(Collections.emptyList())
                .stream()
                .collect(Collectors.toMap(ServiceGroup::getGroupId, r -> r));
        // 服务分类
        ServCategory servCateg = servCategoryService.findByKey(servProduct.getServCategId());
        // 后台产品Map
        List<Integer> productIdList = itemDROList.stream().map(m -> m.getProductId()).distinct().collect(Collectors.toList());
        List<ServProduct> products = servProductService.listByIdList(productIdList);
        Map<Integer, ServProduct> productMap = Optional.ofNullable(products)
                .orElse(Collections.emptyList())
                .stream()
                .collect(Collectors.toMap(ServProduct::getProductId, r -> r));
        // 二级分类Map
        List<Integer> categTwoIdList = products.stream().map(m -> m.getCategId()).distinct().collect(Collectors.toList());
        Map<Integer, BaseCategory> categTwoMap = Optional.ofNullable(baseCategoryService.findByKeys(categTwoIdList))
                .orElse(Collections.emptyList())
                .stream()
                .collect(Collectors.toMap(BaseCategory::getCategId, r -> r));

        PriceCalDTO calDTO = new PriceCalDTO(query.getChannelId(), query.getCityId(), query.getBrandId(), query.getBizType(), null);
        List<ItemDRO3> newItemDROList = itemDROList.parallelStream().peek(itemDRO -> {
            // 计算系数后明细
            meterFlow3CommonBService.calPriceDetail(itemDRO, calDTO);

            // TODO 具体字段待咨询
            itemDRO.setCategId(productMap.get(itemDRO.getProductId()).getCategId());
            itemDRO.setCategName(categTwoMap.get(itemDRO.getCategId()).getName());
            itemDRO.setCategSort(categTwoMap.get(itemDRO.getCategId()).getSort());
            itemDRO.setGroupName(groupMap.get(itemDRO.getGroupId()).getGroupName());
            itemDRO.setGroupSort(groupMap.get(itemDRO.getGroupId()).getSort());
            // 前台产品的属性值需要进行变换，具体的变化规则为：
            // 1.项目名称：后台产品二级分类名称+产品属性
            // 2.项目分组：项目名称
            // 3.产品名称：前台产品名称
            if (Objects.equals(ProductConsts.EC_PRODUCT_TYPE, servProduct.getShowType()) || Objects.equals(ProductConsts.ET_PRODUCT_TYPE, servProduct.getShowType())) {
                itemDRO.setGroupName(itemDRO.getItemName());
                itemDRO.setItemName(getSeqName(itemDRO.getProductId(), productMap.get(itemDRO.getProductId()).getSequence(), itemDRO.getCategId()));
                itemDRO.setProductName(servProduct.getName());
                itemDRO.setServCategName(servCateg.getShowName());
            }
        }).collect(Collectors.toList());
        log.info("项目itemDRO计算价格后数据:{}", newItemDROList);
        return newItemDROList;
    }

    private String getSeqName(Integer productId, String sequence, Integer categId) {
        try {
            return productEnuCache.get(productId, callerItem(sequence, categId));
        } catch (ExecutionException e) {
            log.error("getSeqName cache error.", e);
        }
        return null;
    }

    private Callable<String> callerItem(String sequence, Integer categId) {
        return () -> {
            List<Integer> attrIds = new ArrayList<>();
            if (StringUtil.isNotBlank(sequence) && sequence.indexOf("&") != -1) {
                String[] seqArray = sequence.split(",");
                for (String seq : seqArray) {
                    if (StringUtil.isNotBlank(seq) && seq.indexOf("&") != -1) {
                        String[] enumIdArray = seq.split("&");
                        if (enumIdArray.length > 1 && !Objects.equals(enumIdArray[0], enumIdArray[1])) {
                            attrIds.add(Integer.valueOf(enumIdArray[1]));
                        }
                    }
                }
            }
            List<BaseCategoryAttributeEnum> enumList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(attrIds)) {
                enumList = baseCategoryAttributeEnumService.listByKeys(attrIds);
            }
            StringBuilder stringBuilder = new StringBuilder();
            if (CollectionUtils.isEmpty(enumList)) {
            } else {
                enumList.forEach(e -> {
                    stringBuilder.append(e.getEnumValue()).append(",");
                });
                if (stringBuilder.length() > 0) {
                    stringBuilder.deleteCharAt(stringBuilder.length() - 1);
                }
            }
            String name = Optional.ofNullable(baseCategoryService.findByKey(categId).getName()).orElse("");
            return name + stringBuilder.toString();
        };
    }
}
